﻿using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Data;
using System.Linq;
using Microsoft.SharePoint.Utilities;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;

namespace dpruna.SharePoint
{
    public partial class MultiSelectPagedGridView : UserControl
    {
        #region Events
        public event EventHandler<MultiSelectPagedGridViewSelectEventArgs> DataSelected;
        #endregion

        #region Fields
        private const string SortAscendingViewStateKey = "SortDirection";
        private const string SelectedDataDictionaryViewStateKey = "SelectedDataDictionary";
        private int pageSize = 20;
        private bool SortAscending
        {
            get
            {
                return Convert.ToBoolean(ViewState[SortAscendingViewStateKey] ?? true);
            }
        }
        private Dictionary<int, string> SelectedDataDictionary
        {
            get
            {
                return ViewState[SelectedDataDictionaryViewStateKey] as Dictionary<int, string>;
            }
            set
            {
                ViewState[SelectedDataDictionaryViewStateKey] = value;
            }
        }
        #endregion

        #region Properties
        public Guid ListID { get; set; }
        public string DataKeyField { get; set; }
        public string FieldName { get; set; }
        public string SelectedItemsIDs { get; private set; }
        public string SelectedItemsDisplayNames { get; private set; }
        public int PageSize
        {
            get
            {
                return pageSize;
            }
            set
            {
                if (value < 5)
                    pageSize = 20;

                pageSize = value;
            }
        }
        public bool AllowMultiSelect { get; set; }
        #endregion

        #region Protected Methods
        protected override void OnInit(EventArgs e)
        {
            gridView.DataKeyNames = new[] { DataKeyField };
            gridView.PageSize = PageSize;
            gridView.AllowPaging = false;
            BoundField boundField = gridView.Columns[1] as BoundField;
            if (boundField != null)
            {
                boundField.DataField = FieldName;
                boundField.HeaderText = FieldName;
            }
        }
        protected override void OnLoad(EventArgs e)
        {
            if (!
                Page.IsPostBack)
            {
                ViewState[SortAscendingViewStateKey] = true;
                gridView.PageSize = PageSize;
                gridView.DataKeyNames = new[] { DataKeyField };
                gridView.DataSource = GetDataForPage(gridView.PageIndex, false, FieldName);
                gridView.DataBind();
            }
        }
        #endregion

        #region Private Methods
        private DataTable GetDataForPage(int currentPage, bool backNavigation, string sortField)
        {
            var listItemCollectionPositionString = String.Empty;
            var ctxWeb = SPControl.GetContextWeb(Context);
            var pageItems = new DataTable();
            using (var site = new SPSite(ctxWeb.Site.ID))
            using (var web = site.OpenWeb(ctxWeb.ID))
            {
                var viewFieldsBuilder = new StringBuilder("<FieldRef Name='ID'/>");
                if (!FieldName.Equals("ID", StringComparison.InvariantCultureIgnoreCase))// && !FieldName.Equals("Title", StringComparison.InvariantCultureIgnoreCase))
                    viewFieldsBuilder.AppendFormat("<FieldRef Name='{0}'/>", FieldName);

                var spQuery = new SPQuery
                {
                    Query = String.Format("<OrderBy><FieldRef Name='{0}' Ascending='{1}'/></OrderBy>", FieldName, SortAscending ? "True" : "False"),
                    ViewFields = viewFieldsBuilder.ToString(),
                    RowLimit = (uint)gridView.PageSize
                };

                if (gridView.DataKeys.Count > 0 && gridView.PageIndex > 0)
                {
                    if (backNavigation)
                        listItemCollectionPositionString =
                            String.Format("Paged=TRUE&PagedPrev=TRUE&p_{1}={2}&p_ID={0}&PageLastRow={3}",
                                gridView.DataKeys[0].Value,
                                FieldName, gridView.Rows[0].Cells[1].Text,
                                currentPage * gridView.PageSize + 1);
                    else
                        listItemCollectionPositionString =
                            String.Format("Paged=TRUE&p_{1}={2}&p_ID={0}&PageFirsRow={3}",
                                gridView.DataKeys[gridView.Rows.Count - 1].Value,
                                FieldName, gridView.Rows[gridView.Rows.Count - 1].Cells[1].Text,
                                currentPage * gridView.PageSize + 1);
                }

                if (!
                    String.IsNullOrEmpty(listItemCollectionPositionString))
                {
                    var itemCollectionPosition = new SPListItemCollectionPosition(listItemCollectionPositionString);
                    spQuery.ListItemCollectionPosition = itemCollectionPosition;
                }

                var list = web.Lists[ListID];
                var itemCollection = list.GetItems(spQuery);

                var noOfPages = (int)Math.Ceiling((float)list.ItemCount / gridView.PageSize);
                PrevPageButtonTop.Visible = PrevPageButtonBottom.Visible = (currentPage + 1 > 1);
                NextPageButtonTop.Visible = NextPageButtonBottom.Visible = (currentPage + 1 < noOfPages);
                PagingInfoLabelTop.Text = PagingInfoLabelBottom.Text = String.Format("Page {0} of {1} ({2} item(s))", currentPage + 1, noOfPages, gridView.PageSize);

                pageItems = itemCollection.GetDataTable();
            }

            return pageItems;
        }

        private void AddToSelectedDataDictionary(int id, string displayName)
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary == null)
                selectedDataDictionary = new Dictionary<int, string>();

            if (ExistsInSelectedDataDictionary(id))
                return;

            selectedDataDictionary.Add(id, displayName);

            ViewState[SelectedDataDictionaryViewStateKey] = selectedDataDictionary;
        }
        private bool ExistsInSelectedDataDictionary(int id)
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary == null)
                return false;

            return selectedDataDictionary.ContainsKey(id);
        }
        private void RemoveFromSelectedDataDictionary(int id)
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary == null)
                return;

            if (ExistsInSelectedDataDictionary(id))
                selectedDataDictionary.Remove(id);

            ViewState[SelectedDataDictionaryViewStateKey] = selectedDataDictionary;
        }
        private void ClearSelectedDataDictionary()
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary != null)
                ViewState[SelectedDataDictionaryViewStateKey] = null;
        }
        private void SaveSelectedData()
        {
#if !DEBUG
            try
            {
#endif

            foreach (GridViewRow gridRow in gridView.Rows)
            {
                if (AllowMultiSelect)
                {
                    var chkRowItem = (CheckBox)gridRow.FindControl("chkRow");
                    if (chkRowItem.Checked)
                    {
                        int colIndex;
                        for (colIndex = 0; colIndex < gridView.Columns.Count; colIndex++)
                        {
                            var boundField = gridView.Columns[colIndex] as BoundField;
                            if (boundField != null && boundField.DataField.Equals(FieldName))
                                break;
                        }

                        int id = 0;
                        if (Int32.TryParse((gridView.DataKeys[gridRow.RowIndex].Value ?? String.Empty).ToString(), out id))
                            AddToSelectedDataDictionary(id, gridRow.Cells[colIndex].Text);
                    }
                }
                else
                {
                    ClearSelectedDataDictionary();

                    var rbRowItem = (RadioButton)gridRow.FindControl("rbRow");
                    if (rbRowItem.Checked)
                    {
                        int colIndex;
                        for (colIndex = 0; colIndex < gridView.Columns.Count; colIndex++)
                        {
                            var boundField = gridView.Columns[colIndex] as BoundField;
                            if (boundField != null && boundField.DataField.Equals(FieldName))
                                break;
                        }

                        int id = 0;
                        if (Int32.TryParse((gridView.DataKeys[gridRow.RowIndex].Value ?? String.Empty).ToString(), out id))
                            AddToSelectedDataDictionary(id, gridRow.Cells[colIndex].Text);
                        break;
                    }
                }
            }

#if !DEBUG
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.ToString());
            }
#endif

        }
        private void GetSelectedDataDictionaryData(StringBuilder idsBuilder, StringBuilder dislayNamesBuilder)
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary == null)
                return;

            foreach (KeyValuePair<int, string> kvp in selectedDataDictionary)
            {
                idsBuilder.AppendFormat("{0}{1}", kvp.Key, Helpers.SelectedDataSeparator);
                dislayNamesBuilder.AppendFormat("{0}{1}", kvp.Value, Helpers.SelectedDataSeparator);
            }
        }
        private void GetSelectedData(out string ids, out string displayNames)
        {
            var idsBuilder = new StringBuilder();
            var displayNamesBuilder = new StringBuilder();

#if !DEBUG
            try
            {
#endif

            GetSelectedDataDictionaryData(idsBuilder, displayNamesBuilder);

            foreach (GridViewRow gridRow in gridView.Rows)
            {
                if (AllowMultiSelect)
                {
                    var chkRowItem = (CheckBox)gridRow.FindControl("chkRow");
                    if (chkRowItem.Checked)
                    {
                        idsBuilder.AppendFormat("{0}{1}", gridView.DataKeys[gridRow.RowIndex].Value, Helpers.SelectedDataSeparator);

                        int colIndex;
                        for (colIndex = 0; colIndex < gridView.Columns.Count; colIndex++)
                        {
                            var boundField = gridView.Columns[colIndex] as BoundField;
                            if (boundField != null && boundField.DataField.Equals(FieldName))
                                break;
                        }

                        displayNamesBuilder.AppendFormat("{0}{1}", gridRow.Cells[colIndex].Text, Helpers.SelectedDataSeparator);

                        int id = 0;
                        if (Int32.TryParse((gridView.DataKeys[gridRow.RowIndex].Value ?? String.Empty).ToString(), out id))
                            AddToSelectedDataDictionary(id, gridRow.Cells[colIndex].Text);
                    }
                }
                else
                {
                    var rbRowItem = (RadioButton)gridRow.FindControl("rbRow");
                    if (rbRowItem.Checked)
                    {
                        ClearSelectedDataDictionary();
                        idsBuilder = new StringBuilder();
                        displayNamesBuilder = new StringBuilder();

                        idsBuilder.Append(gridView.DataKeys[gridRow.RowIndex].Value);

                        int colIndex;
                        for (colIndex = 0; colIndex < gridView.Columns.Count; colIndex++)
                        {
                            var boundField = gridView.Columns[colIndex] as BoundField;
                            if (boundField != null && boundField.DataField.Equals(FieldName))
                                break;
                        }
                        displayNamesBuilder.Append(gridRow.Cells[colIndex].Text);

                        int id = 0;
                        if (Int32.TryParse((gridView.DataKeys[gridRow.RowIndex].Value ?? String.Empty).ToString(), out id))
                            AddToSelectedDataDictionary(id, gridRow.Cells[colIndex].Text);
                        break;
                    }
                }
            }

#if !DEBUG
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.ToString());
            }
#endif

            ids = idsBuilder.ToString().TrimEnd(Helpers.SelectedDataSeparator.ToCharArray());
            displayNames = displayNamesBuilder.ToString().TrimEnd(Helpers.SelectedDataSeparator.ToCharArray());
        }
        #endregion

        #region Registered Events
        protected void SelectItemsButton_Click(object sender, EventArgs e)
        {
            if (DataSelected != null)
            {
                string ids, displayNames;
                GetSelectedData(out ids, out displayNames);
                DataSelected(this, new MultiSelectPagedGridViewSelectEventArgs(ids, displayNames));
            }
        }

        protected void gridView_Sorting(object sender, GridViewSortEventArgs e)
        {
            SaveSelectedData();

            gridView.PageIndex = 0;
            ViewState[SortAscendingViewStateKey] = !SortAscending;
            gridView.DataSource = GetDataForPage(gridView.PageIndex, false, FieldName);
            gridView.DataBind();
        }

        protected void NextPageButton_Click(object sender, EventArgs e)
        {
            SaveSelectedData();

            gridView.PageIndex++;
            gridView.DataSource = GetDataForPage(gridView.PageIndex, false, FieldName);
            gridView.DataBind();
        }
        protected void PreviousPageButton_Click(object sender, EventArgs e)
        {
            SaveSelectedData();

            gridView.PageIndex--;
            gridView.DataSource = GetDataForPage(gridView.PageIndex, true, FieldName);
            gridView.DataBind();
        }

        protected void gridView_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            var selectedDataDictionary = SelectedDataDictionary;
            if (selectedDataDictionary == null)
                return;

            if (e.Row.RowType != DataControlRowType.DataRow)
                return;

            if (AllowMultiSelect)
            {
                var chkRowItem = (CheckBox)e.Row.FindControl("chkRow");
                int id = 0;
                if (Int32.TryParse((gridView.DataKeys[e.Row.RowIndex].Value ?? String.Empty).ToString(), out id) && selectedDataDictionary.ContainsKey(id))
                    chkRowItem.Checked = true;
                else
                    chkRowItem.Checked = false;
            }
            else
            {
                var rbRowItem = (RadioButton)e.Row.FindControl("rbRow");
                int id = 0;
                if (Int32.TryParse((gridView.DataKeys[e.Row.RowIndex].Value ?? String.Empty).ToString(), out id) && selectedDataDictionary.ContainsKey(id))
                    rbRowItem.Checked = true;
                else
                    rbRowItem.Checked = false;

            }
        }
        #endregion
    }
}
